package me.itsoo.artemis.framework.core.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.itsoo.artemis.framework.core.exception.ElementNotFoundException;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * CollectionUtils
 *
 * @author zxy
 * @version 0.0.1
 * @since 2023/5/11
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CollectionUtils {

    /**
     * 集合为空
     *
     * @param coll Collection
     * @return boolean
     */
    public static boolean isEmpty(Collection<?> coll) {
        return (coll == null) || coll.isEmpty();
    }

    /**
     * 集合为空
     *
     * @param map Map
     * @return boolean
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return (map == null) || map.isEmpty();
    }

    /**
     * 集合不为空
     *
     * @param coll Collection
     * @return boolean
     */
    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }

    /**
     * 集合不为空
     *
     * @param map Map
     * @return boolean
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * 查找集合中第一个元素
     *
     * @param coll Collection
     * @param <T>  T
     * @return T
     */
    public static <T> T findFirst(Collection<T> coll) {
        if (isEmpty(coll)) {
            return null;
        }

        final Iterator<T> it = coll.iterator();
        if (it.hasNext()) {
            return it.next();
        }

        return null;
    }

    /**
     * 查找集合中第一个元素
     *
     * @param map Map
     * @param <K> K
     * @param <V> V
     * @return V
     */
    public static <K, V> V findFirst(Map<K, V> map) {
        if (isEmpty(map)) {
            return null;
        }

        final Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
        if (it.hasNext()) {
            return it.next().getValue();
        }

        return null;
    }

    /**
     * 查找集合中第一个元素（非空）
     *
     * @param coll Collection
     * @param <T>  T
     * @return T
     */
    public static <T> T findFirstNonNull(Collection<T> coll) {
        final T result = findFirst(coll);
        if (result == null) {
            throw new ElementNotFoundException();
        }

        return result;
    }

    /**
     * 查找集合中第一个元素（非空）
     *
     * @param map Map
     * @param <K> K
     * @param <V> V
     * @return V
     */
    public static <K, V> V findFirstNonNull(Map<K, V> map) {
        final V result = findFirst(map);
        if (result == null) {
            throw new ElementNotFoundException();
        }

        return result;
    }

    /**
     * 查找集合中第一个元素
     *
     * @param coll Collection
     * @param <T>  T
     * @return Optional
     */
    public static <T> Optional<T> findFirstOptional(Collection<T> coll) {
        return Optional.ofNullable((findFirst(coll)));
    }

    /**
     * 查找集合中第一个元素
     *
     * @param map Map
     * @param <K> K
     * @param <V> V
     * @return Optional
     */
    public static <K, V> Optional<V> findFirstOptional(Map<K, V> map) {
        return Optional.ofNullable((findFirst(map)));
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll Collection
     * @param pred Predicate
     * @param <T>  T
     * @return List
     */
    public static <T> List<T> filter(List<T> coll, Predicate<T> pred) {
        if (isEmpty(coll)) {
            return coll;
        }

        coll.removeIf(t -> pred.negate().test(t));
        return coll;
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll Collection
     * @param pred Predicate
     * @param <T>  T
     * @return Set
     */
    public static <T> Set<T> filter(Set<T> coll, Predicate<T> pred) {
        if (isEmpty(coll)) {
            return coll;
        }

        coll.removeIf(t -> pred.negate().test(t));
        return coll;
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll Collection
     * @param pred Predicate
     * @param <T>  T
     * @return Collection
     */
    public static <T> Collection<T> filter(Collection<T> coll, Predicate<T> pred) {
        if (isEmpty(coll)) {
            return coll;
        }

        coll.removeIf(t -> pred.negate().test(t));
        return coll;
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll  Collection
     * @param pred1 Predicate
     * @param pred2 Predicate
     * @param <T>   T
     * @return List
     */
    public static <T> List<T> filterOr(List<T> coll, Predicate<T> pred1, Predicate<T> pred2) {
        return filter(coll, pred1.or(pred2));
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll  Collection
     * @param pred1 Predicate
     * @param pred2 Predicate
     * @param <T>   T
     * @return Set
     */
    public static <T> Set<T> filterOr(Set<T> coll, Predicate<T> pred1, Predicate<T> pred2) {
        return filter(coll, pred1.or(pred2));
    }

    /**
     * 过滤集合中的元素
     *
     * @param coll  Collection
     * @param pred1 Predicate
     * @param pred2 Predicate
     * @param <T>   T
     * @return Collection
     */
    public static <T> Collection<T> filterOr(Collection<T> coll, Predicate<T> pred1, Predicate<T> pred2) {
        return filter(coll, pred1.or(pred2));
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return List
     */
    public static <T, R> List<R> transform(List<T> coll, Function<T, R> func) {
        if (isEmpty(coll) || func == null) {
            return Collections.emptyList();
        }

        return coll.stream().map(func).collect(Collectors.toList());
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return Set
     */
    public static <T, R> Set<R> transform(Set<T> coll, Function<T, R> func) {
        if (isEmpty(coll) || func == null) {
            return Collections.emptySet();
        }

        return coll.stream().map(func).collect(Collectors.toSet());
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return Collection
     */
    public static <T, R> Collection<R> transform(Collection<T> coll, Function<T, R> func) {
        if (isEmpty(coll) || func == null) {
            return Collections.emptyList();
        }

        return coll.stream().map(func).collect(Collectors.toSet());
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return List
     */
    public static <T, R> List<R> transformNonNull(List<T> coll, Function<T, R> func) {
        return transform(coll, func).stream().filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return Set
     */
    public static <T, R> Set<R> transformNonNull(Set<T> coll, Function<T, R> func) {
        return transform(coll, func).stream().filter(Objects::nonNull).collect(Collectors.toSet());
    }

    /**
     * 集合元素类型转换
     *
     * @param coll Collection
     * @param func Function
     * @param <T>  T
     * @param <R>  R
     * @return Collection
     */
    public static <T, R> Collection<R> transformNonNull(Collection<T> coll, Function<T, R> func) {
        return transform(coll, func).stream().filter(Objects::nonNull).collect(Collectors.toSet());
    }

    /**
     * 任意匹配元素
     *
     * @param coll Collection
     * @param pred Predicate
     * @param <T>  T
     * @return boolean
     */
    public static <T> boolean anyMatch(Collection<T> coll, Predicate<T> pred) {
        return coll.stream().anyMatch(pred);
    }

    /**
     * 全部匹配元素
     *
     * @param coll Collection
     * @param pred Predicate
     * @param <T>  T
     * @return boolean
     */
    public static <T> boolean allMatch(Collection<T> coll, Predicate<T> pred) {
        return coll.stream().allMatch(pred);
    }

    /**
     * 转换为 Map 集合
     *
     * @param coll Collection
     * @param <T>  T
     * @return Map
     */
    @SuppressWarnings("unchecked")
    public static <T> Map<Class<T>, T> toMap(Collection<T> coll) {
        final Map<Class<T>, T> result = new HashMap<>(coll.size());
        coll.forEach(t -> result.put((Class<T>) t.getClass(), t));
        return result;
    }
}
